#ifndef NETCLIENT_H
#define NETCLIENT_H

#include "../model/data.h"
#include <QObject>
#include <QNetworkAccessManager>
#include <QWebSocket>
#include <QNetworkReply>
#include <QProtobufSerializer>

//为了避免循环包含 而进行的前置声明
namespace model
{
    class DataCenter;
}// namespace model

namespace network
{
class NetClient : public QObject
{
    Q_OBJECT
public:
    explicit NetClient(model::DataCenter* dataCenter);

    //验证网络连通性
    void ping();
/////////////////////////////////////////////////////////////////////////////////
/// 下列逻辑都是 Websocket 相关的操作
/////////////////////////////////////////////////////////////////////////////////
    //初始化 websocket
    void initWebsocket();

    //发送身份认证请求 该请求服务端不用回复
    void sendAuth();

    //处理收到的 websocket 消息
    void handleWebsocketNotifyMessage(const xchat::NotifyMessage& notifyMessage);

    //处理收到的 websocket 消息 具体的 Message
    void handleWebsocketMessage(const model::Message& message);

    //处理收到的 删除好友 通知
    void handleWebsocketRemoveFriend(const QString& removedFriendUserId);

    //处理添加好友申请的通知
    void handleWebsocketAddFriendApply(const model::UserInfo& fromUserInfo);

    //好友申请处理结果的通知
    void handleWebsocketFriendApplyProcess(const model::UserInfo& fromUserInfo, bool isAccepted);

    //创建群聊会话的通知
    void handleWebsocketCreateGroupChatSession(const model::ChatSessionInfo& newSessionInfo);
/////////////////////////////////////////////////////////////////////////////////
/// 下列逻辑都是 http 相关的操作 服务接口 userid是服务器和服务器之间交互的字段 客户端和gateway只需要sessionid即可
/////////////////////////////////////////////////////////////////////////////////
    //生成请求ID
    static QString makeRequestId();

    //封装发送请求的逻辑
    QNetworkReply* sendHttpRequest(const QString& apiPath, const QByteArray& body);

    //封装处理响应的逻辑(判定http正确性 反序列化正确性 业务正确性)
    //由于不同的api 返回的数据结构不一样 需要使用模板 模板函数不支持分离编译模型 声明定义必须在其一起
    template<typename T>
    std::shared_ptr<T> handleHttpResponse(QNetworkReply* httpResp, bool *ok, QString* reason)
    {
        if (httpResp->error() != QNetworkReply::NoError) {
            // 请求失败!
            *ok = false;
            *reason = "HTTP 请求失败! " + httpResp->errorString();
            httpResp->deleteLater();
            return std::shared_ptr<T>();
        }
        // 获取到响应的 body
        QByteArray body = httpResp->readAll();
        // 反序列化 protobuf
        std::shared_ptr<T> resp = std::make_shared<T>();
        bool parseOk = resp->deserialize(&serializer, body);
        if (!parseOk) {
            *ok = false;
            *reason = "响应反序列化失败!";
            httpResp->deleteLater();
            return std::shared_ptr<T>();
        }
        // 业务判定
        if (resp->success() == false) {
            *ok = false;
            *reason = resp->errmsg();
            httpResp->deleteLater();
            return std::shared_ptr<T>();
        }
        // 一切正常
        httpResp->deleteLater();
        *ok = true;
        return resp;
    }
    // 获取当前用户信息
    void getMyself(const QString& loginSessionId);
    // 获取好友列表
    void getFriendList(const QString& loginSessionId);
    // 获取会话列表
    void getChatSessionList(const QString& loginSessionId);
    // 获取好友申请列表
    void getFriendApplyList(const QString& loginSessionId);
    // 获取最近消息列表
    void getRecentMessage(const QString& loginSessionId, const QString& chatSessionId, bool updateUI);
    // 发送消息
    void sendMessage(const QString& loginSessionId, const QString& chatSessionId,
                     const model::Message_Type messageType, const QByteArray& content, const QString& extraInfo);
    //处理接受消息
    void receiveMessage(const QString& chatSessionId);
    // 修改用户昵称
    void changeNickname(const QString& loginSessionId, const QString& newNickname);
    // 修改用户签名
    void changeDescription(const QString& loginSessionId, const QString& newDescription);
    // 获取验证码
    void getVerifyCode(const QString& phoneNumber);
    // 尝试修改手机号
    void changePhoneNumber(const QString& loginSessionId, const QString& newPhoneNumber, const QString& verifyCodeId, const QString& userInputCode);
    // 修改头像
    void changeAvatar(const QString& loginSessionId, const QByteArray& newAvatarData);
    // 删除好友
    void deleteFriend(const QString& loginSessionId, const QString& friendUserId);
    // 添加好友
    void addFriendApply(const QString& loginSessionId, const QString& friendUserId);
    // 同意好友申请
    void acceptFriendApply(const QString& loginSessionId, const QString& friendUserId);
    // 拒绝好友申请
    void rejectFriendApply(const QString& loginSessionId, const QString& friendUserId);
    // 创建群聊会话
    void createGroupChatSession(const QString& loginSessionId, const QList<QString>& memberUserIds);
    // 获取群聊会话成员列表
    void getMemberList(const QString& loginSessionId, const QString& chatSessionId);
    // 发起搜索用户请求
    void searchUser(const QString& loginSessionId, const QString& keyword);
    // 搜索历史消息
    void searchHistoryMessage(const QString& loginSessionId, const QString& chatSessionId, const QString& keyword);
    // 按时间搜索历史消息
    void searchHistoryMessageByTime(const QString& loginSessionId, const QString& chatSessionId, const QDateTime& begTime, const QDateTime& endTime);
    // 用户登录请求
    void userLogin(const QString& userName, const QString& password);
    // 用户注册
    void userRegister(const QString& userName, const QString& password);
    // 用户手机号登录
    void phoneLogin(const QString& phoneNumber, const QString& verifyCodeId, const QString& verifyCode);
    // 用户手机号注册
    void phoneRegister(const QString& phoneNumber, const QString& verifyCodeId, const QString& verifyCode);
    // 获取单个文件
    void getSingleFile(const QString& loginSessionId, const QString& fileId);
    // 语音转文字
    void speechToText(const QString &loginSessionId, const QString &fileId, const QByteArray& speechData);

private:
    // 定义重要常量. ip 都暂时使用本地的环回 ip. 端口号约定成 9000 和 9001
    const QString HTTP_URL = "http://43.140.203.198:9000";
    const QString WEBSOCKET_URL = "ws://43.140.203.198:9001/ws";

private:

    model::DataCenter* dataCenter;
    // http 客户端
    QNetworkAccessManager httpClient;
    // WebSocket 客户端
    QWebSocket websocketClient;
    // 序列化器
    QProtobufSerializer serializer;


signals:
};

}   // namespace network

#endif // NETCLIENT_H
